home *** CD-ROM | disk | FTP | other *** search
/ Aminet 21 / Aminet 21 (1997)(GTI - Schatztruhe)[!][Oct 1997].iso / Aminet / comm / bbs / cit_src_7H21.lha / util_domains.c < prev    next >
C/C++ Source or Header  |  1997-08-15  |  38KB  |  1,381 lines

  1. /*
  2. *                               Domains.C
  3. *
  4. * Domain handing code.
  5. */
  6. #include "ctdl.h"
  7. #include "2ndfmt.h"
  8. /*
  9. *                               history
  10. *
  11. * 90May20 HAW  Created.
  12. */
  13. /*
  14. *                               contents
  15. *
  16. *       DomainInit()            Initializes domains.  Call once only.
  17. *       EatDomainLine()         Eats a line from map.sys.
  18. *       CheckDomain()           Helps search lists for a domain.
  19. *       CmpDomain()             Helps sort a list of domains.
  20. *       DomainMailFileName()    Creates a filename for mail for a domain.
  21. *       GetDomain()             Create a domain entry for domain mail.
  22. *       UpdateMap()             Updates Map.sys.
  23. *       WriteDomainMap()        Helps write out Map.sys.
  24. *       DomainFileAddResult()   Finishes dealing with a piece of domain mail.
  25. *       RationalizeDomains()    Checks all active domains for integrity.
  26. *       DomainRationalize()     Checks a single domain for integrity.
  27. *       KillDomain()            Takes a domain out of list and disk.
  28. *       FindDomainH()           Helps find a domain handler in a list.
  29. *       EatDomainH()            Eats a line from ctdldmhd.sys.
  30. *       ViaHandle()             Handles a 'via' line from ctdldmhd.sys.
  31. *       HandleExistingDomain()  Process a domain we serve.
  32. *       SetCallout()            Sets callout flags depending on domains.
  33. *       SetUpCallOut()          Finds direction to sling domain mail.
  34. *       CallOutWork()           Work fn to find direction ...
  35. *       ClearSearched()         Clears a search marker for each domain.
  36. *       DomainOut()             Manages transmission of domain mail.
  37. *       SendDomainMail()        Does actual work of transmitting domain mail.
  38. *       SystemInSecondary()     Check for name in secondary list(s).
  39. *       SearchSecondary()       Work fn for above.
  40. *       EatCosts()              Eat a line from ctdlcost.sys.
  41. *       FindCost()              Helps find the cost for a given domain.
  42. *       WriteDomainContents()   Handles .EN? in Mail.
  43. *       DomainLog()             Handles the file DOMAIN.LOG.
  44. *       RouteHere()             Is domain mail meant for here?
  45. *       LocalName()             Is given system local?
  46. *       lifo()                  Last in First Out fn for the lists.
  47. */
  48. #define MAPSYS          "map.sys"
  49. #define WeServe(x)      SearchList(&Serves, x)
  50. /*
  51. * Required Global Variables
  52. */
  53. /*
  54. * DomainDir
  55. *
  56. * Variables of this sort define the mapping between a domain name and the
  57. * directory containing outstanding mail bound for that domain.  Typically,
  58. * a domain with no mail will not have an allocated variable.
  59. *
  60. * UsedFlag - a temporary flag indicating that domain mail was sent during
  61. * the last network connection.  Used for performance reasons.
  62. *
  63. * Domain - The name of the domain represented by this record.
  64. *
  65. * MapDir - The "name" of the directory containing the mail for this domain.
  66. * This is a number; each domain is given a unique number amongst those
  67. * active, starting with 0.  When access to the mail is desired, this number
  68. * is used to form the name.
  69. *
  70. * HighFile - The next file name (also formed from numbers starting at 0) to
  71. * use for incoming mail for this domain.
  72. *
  73. * TargetSlot - Index into CtdlNet.Sys indicating the system to pass this mail
  74. * to.  When we're connected with a system and its call out flags indicate
  75. * domain mail is bound for it, we use this flag to quickly find if this
  76. * domain is bound for the current node connection.  The flag is set when
  77. * the system initially comes up (or the record is created) by checking
  78. * against the contents of CTDLDMHD.SYS.
  79. */
  80. typedef struct
  81.   {
  82.   char   UsedFlag;      /* indicates domain was sent during last call */
  83.   label  Domain;                /* name */
  84.   UNS_16 MapDir;                /* dirnum */
  85.   UNS_16 HighFile;      /* next filename */
  86.   int    TargetSlot;    /* who do we pass this stuff on to */
  87.  
  88.   }
  89. DomainDir;
  90. DomainDir *GetDomain(char *DName, char create);
  91. /*
  92. * DomainHandler
  93. *
  94. * Defines a domain handler.  Records of this type are read in from
  95. * ctdldmhd.sys and its brethren.
  96. *
  97. * domain - Name of the domain.
  98. *
  99. * nodeId - ID of the domain server for the domain.
  100. *
  101. * searched - Temporary flag indicating if the attempt to trace the next
  102. * system to attempt to find in CtdlNet.Sys has already been checked.  Lets
  103. * us avoid infinite loops.
  104. *
  105. * via - A hint on how to connect to this system.
  106. */
  107. typedef struct Dh
  108.   {
  109.   char          *domain;
  110.   char          *nodeId;
  111.   char          searched;       /* avoids infinite loops        */
  112.   struct Dh     *via;           /* how to reach this domain     */
  113.  
  114.   }
  115. DomainHandler;
  116. /*
  117. * Some useful static functions.
  118. */
  119. static void  WriteDomainMap(DomainDir *data);
  120. static void  KillDomain(int *MapDir);
  121. static void  DomainRationalize(DomainDir *data);
  122. static void *FindDomainH(DomainHandler *data, char *name);
  123. static void *EatDomainH(char *line);
  124. static void  DomainLog(char *str);
  125. static void *EatCosts(char *line);
  126. static void *CheckDomain(DomainDir *d, char *str);
  127.        void *EatDomainLine(char *line);
  128.        int   CmpDomain(DomainDir *s, DomainDir *t);
  129. static int SetUpCallOut(char *DName);
  130. static int CallOutWork(char *DName);
  131. static int lifo(void);
  132. static void *ViaHandle(char *line);
  133. /*
  134. * List of domain handlers -- source: CTDLDMHD.SYS, CTDLDMHD.LCL.
  135. */
  136. static SListBase DomainHandlers =
  137.   {
  138.   NULL, FindDomainH, (int (*)(void *, void *))lifo, free, EatDomainH
  139.  
  140.   };
  141. /*
  142. * List of costs for domains (from ctdlcost.sys).
  143. */
  144. static SListBase Costs =
  145.   {
  146.   NULL, ChkStrtoN, NULL, NULL, EatCosts
  147.  
  148.   };
  149. /*
  150. * Current domain mail in #DOMAINAREA -- map of directories to domain names.
  151. */
  152. SListBase DomainMap    =
  153.   {
  154.   NULL, CheckDomain, CmpDomain, free, EatDomainLine
  155.  
  156.   };
  157. char *DomainFlags      = NULL;
  158. UNS_16 UnknownCost     = 1;
  159. extern CONFIG    cfg;
  160. extern NetBuffer netBuf, netTemp;
  161. extern NetTable  *netTab;
  162. extern FILE      *upfd, *netMisc, *netLog;
  163. extern int       thisNet;
  164. extern MessageBuffer   msgBuf;
  165. extern logBuffer logBuf;
  166. extern label     HomeId;
  167. extern char      *READ_ANY, *WRITE_TEXT;
  168. extern int       RMcount;
  169. extern SListBase Serves;
  170. extern char      inNet;
  171. /*
  172. * This code is responsible for managing the domain part of Citadel-86.
  173. *
  174. * DOMAIN DIRECTORY:
  175. *    The domain directory consists of a single file and a collection of
  176. * directories.
  177. *
  178. * MAP.SYS: this contains a mapping between the directory names and the
  179. * domain mail they purport to store.  Format:
  180. *
  181. * <number><space><domain><highest filename>
  182. *
  183. * DIRECTORIES (1, 2,...): these contain the actual outgoing mail, plus a file
  184. * named "info" which will hold the name of the domain (for rebuilding
  185. * purposes).
  186. */
  187. /*
  188. * DomainInit()
  189. *
  190. * This function initializes the domain stuff.  It's called from NetInit(),
  191. * addNetNode(), and from RationalizeDomains, so it has to open the relevant
  192. * files only once.
  193. *
  194. * Appropriate lists are created, call out flags set, etc.
  195. */
  196. void DomainInit(char FirstTime)
  197.   {
  198.   void HandleExistingDomain(), SetCallout();
  199.   SYS_FILE temp;
  200.   if (FirstTime)
  201.     {
  202.     makeSysName(temp, MAPSYS, &cfg.domainArea);
  203.     MakeList(&DomainMap, temp, NULL);
  204.     makeSysName(temp, "ctdlcost.sys", &cfg.netArea);
  205.     MakeList(&Costs, temp, NULL);
  206.     makeSysName(temp, "ctdldmhd.sys", &cfg.netArea);
  207.     MakeList(&DomainHandlers, temp, NULL);
  208.     makeSysName(temp, "ctdldmhd.lcl", &cfg.netArea);
  209.     MakeList(&DomainHandlers, temp, NULL);
  210.     /* now we should scan for any domain we serve, and if any are there */
  211.     /* we need to arrange to send that mail to their recipients.        */
  212.     RunList(&DomainMap, HandleExistingDomain);
  213.     RationalizeDomains();
  214.  
  215.     }
  216.   else
  217.   if (DomainFlags != NULL) free(DomainFlags);
  218.   if (cfg.netSize != 0)
  219.   DomainFlags = GetDynamic(cfg.netSize);        /* auto-zeroed */
  220.   /* This will also set the temporary call out flags, eh?               */
  221.   RunList(&DomainMap, SetCallout);
  222.  
  223.   }
  224. /*
  225. * EatDomainLine()
  226. *
  227. * This function parses a line from map.sys.  Map.sys is kept in the
  228. * #DOMAINAREA and maps the from the name of each domain with outstanding
  229. * mail and the directory it resides in.  Directories are numerically
  230. * designated.  Format of each line is
  231. *
  232. * <directory name> <domain name> <last file number>
  233. *
  234. * This function, since it's used by calls to MakeList, returns a void pointer
  235. * to the assembled data structure.
  236. */
  237. void *EatDomainLine(char *line)
  238.   {
  239.   DomainDir *data;
  240.   char *c;
  241.   data = GetDynamic(sizeof *data);
  242.   data->UsedFlag   = FALSE;
  243.   data->TargetSlot = -1;
  244.   if ((c = strchr(line, ' ')) == NULL)
  245.     {
  246.     free(data);
  247.     return NULL;
  248.  
  249.     }
  250.   *c++ = 0;
  251.   data->MapDir = atoi(line);
  252.   line = c;
  253.   if ((c = strchr(line, ' ')) == NULL)
  254.     {
  255.     free(data);
  256.     return NULL;
  257.  
  258.     }
  259.   *c++ = 0;
  260.   strCpy(data->Domain, line);
  261.   data->HighFile = atoi(c);
  262.   return data;
  263.  
  264.   }
  265. static char CheckNumber = FALSE;        /* double duty switcheroonie    */
  266. /*
  267. * CheckDomain()
  268. *
  269. * This is used to look for a domain in a list.  The search can either be on
  270. * the directory number or the domain name.
  271. */
  272. static void *CheckDomain(DomainDir *d, char *str)
  273.   {
  274.   int *i;
  275.   if (!CheckNumber)
  276.     {
  277.     if (strCmpU(d->Domain, str) == SAMESTRING) return d;
  278.  
  279.     }
  280.   else
  281.     {
  282.     i = (int *) str;
  283.     if (*i == d->MapDir) return d;
  284.  
  285.     }
  286.   return NULL;
  287.  
  288.   }
  289. /*
  290. * CmpDomain()
  291. *
  292. * This function is used in the list of domains with mail to keep the domains
  293. * in * numerical order.  The list is kept in numerical order to make MAP.SYS
  294. * a bit more tractable.
  295. */
  296. int CmpDomain(DomainDir *s, DomainDir *t)
  297.   {
  298.   return (int)(s->MapDir - t->MapDir);
  299.  
  300.   }
  301. /*
  302. * DomainMailFileName()
  303. *
  304. * This function is called when FR 9 has been received: someone wants to send
  305. * us routemail with a domain attached.  We are responsible for creating a
  306. * good filename for it, setting flags etc.  Return REFUSE if we can't accept
  307. * mail (for fatal or other reasons), LOCALROUTE if we are the domain server
  308. * and we know the target, OURS if we are the target, DOMAINFILE if we need
  309. * to store the file in a domain directory.
  310. * NB: the caller of this function provides security.
  311. * See DomainFileResult().
  312. * NB: this can also be called for local users sending domain mail
  313. */
  314. char DomainMailFileName(DOMAIN_FILE buffer, label DName, label NodeId,
  315. label NodeName)
  316.   {
  317.   DomainDir *data;
  318.   char temp[10];
  319.   /*
  320.   * The first clause of this if statement is necessary for the following
  321.   * scenario.  If we're in net mode, suppose the caller is conveying
  322.   * domain mail which, as it happens, it will be the route for the
  323.   * final leg of the mail.  (Yes, this actually happened in practice --
  324.   * an LD node was accessible only via a local node to the hub.)  Now,
  325.   * if this if isn't structured this way, when the incoming domain mail
  326.   * is deciphered, it will be assigned to this node WHILE the call is
  327.   * active, including the activation of the mail routing flag.  When the
  328.   * call terminates, these same flags will be turned OFF.  Net result
  329.   * (if you'll pardon the pun) is that R*.* files will be left in #NETAREA
  330.   * and will NOT be delivered.  Tsk tsk.  Therefore, in doResults(),
  331.   * a call to RationalizeDomains() is made which will clean up the domain
  332.   * directories after a call.
  333.   */
  334.   if (cfg.BoolFlags.debug)
  335.     {
  336.     splitF(netLog, "DomainMail(%d, %s, %s, %s)\n",buffer,DName,NodeId,NodeName);
  337.     };
  338.  
  339.   if (inNet == NON_NET && WeServe(DName) != NULL)
  340.     {
  341.     /* since we're server, we don't search the secondary lists */
  342.     if (FindTheNode(NodeId, NodeName))
  343.       {
  344.       if (netTemp.nbflags.RouteTo)
  345.         {
  346.         strCpy(NodeId, netTemp.netId);
  347.         if (cfg.BoolFlags.debug)
  348.           {
  349.           splitF(netLog, "result LOCALROUTE: NodeId=%s\n",NodeId);
  350.           };
  351.         return LOCALROUTE;
  352.  
  353.         }
  354.  
  355.       }
  356.     /*
  357.     * don't return if either conditional fails.  if we don't know
  358.     * about the node, still accept the mail for delivery, and see
  359.     * the use of HandleExistingDomain(), which is called each time
  360.     * the system is brought up.  If it's a node we know about but
  361.     * don't like, accept the mail anyways -- just in case we changes
  362.     * our mindses.  if we don't, well, hey, too bad.
  363.     */
  364.  
  365.     }
  366.   /*
  367.   * rare, but possible case - mail meant for us but we don't serve the
  368.   * domain, it's just getting routed via us.  this also picks up all
  369.   * cases where domain mail is being sent to the domain-server.
  370.   */
  371.   NormStr(NodeId);
  372.   if (RouteHere(NodeId, NodeName, DName))
  373.     {
  374.     if (cfg.BoolFlags.debug)
  375.       {
  376.       splitF(netLog, "result Ours\n");
  377.       };
  378.     return OURS;
  379.  
  380.     }
  381.   /* ok, we need to store it in its proper domain */
  382.   data = GetDomain(DName, TRUE);        /* GetDomain() creates, etc.    */
  383.   sPrintf(temp, "%d", data->HighFile++);
  384.   MakeDomainFileName(buffer, data->MapDir, temp);
  385.   UpdateMap();
  386.   return DOMAINFILE;
  387.  
  388.   }
  389. static FILE *fd;
  390. /*
  391. * GetDomain()
  392. *
  393. * This function is used to find the given domain in the installation's
  394. * current outstanding mail.  If no such domain exists, then create the
  395. * data structure and the directory entry.  In either case, return a pointer
  396. * to the appropriate record.  This should never return NULL.
  397. */
  398. DomainDir *GetDomain(char *DName, char create)
  399.   {
  400.   DomainDir *data;
  401.   int i;
  402.   DOMAIN_FILE buf;
  403.   if ((data = SearchList(&DomainMap, DName)) == NULL)
  404.     {
  405.     if (!create) return NULL;
  406.     /* create it */
  407.     data = GetDynamic(sizeof *data);
  408.     data->UsedFlag = FALSE;
  409.     strCpy(data->Domain, DName);
  410.     data->HighFile = 0;
  411.     CheckNumber = TRUE;
  412.     for (i = 0; i < 10000; i++)
  413.     if (SearchList(&DomainMap, &i) == NULL) break;
  414.     data->MapDir = i;
  415.     AddData(&DomainMap, data, NULL, FALSE);
  416.     MakeDomainDirectory(i);
  417.     MakeDomainFileName(buf, data->MapDir, "info");
  418.     if ((fd = safeopen(buf, WRITE_TEXT)) != NULL)
  419.       {
  420.       fprintf(fd, "%s", DName);
  421.       fclose(fd);
  422.  
  423.       }
  424.     CheckNumber = FALSE;
  425.     /* UpdateMap();        don't update -- let caller do it */
  426.  
  427.     }
  428.   return data;
  429.  
  430.   }
  431. /*
  432. * UpdateMap()
  433. *
  434. * This function is charged with updating MAP.SYS with new information.
  435. * Most of the work is done in WriteDomainMap().
  436. */
  437. void UpdateMap()
  438.   {
  439.   SYS_FILE temp;
  440.   void WriteDomainMap();
  441.   makeSysName(temp, MAPSYS, &cfg.domainArea);
  442.   if ((fd = safeopen(temp, WRITE_TEXT)) != NULL)
  443.     {
  444.     RunList(&DomainMap, WriteDomainMap);
  445.     fclose(fd);
  446.  
  447.     }
  448.  
  449.   }
  450. /*
  451. * WriteDomainMap()
  452. *
  453. * This writes out an entry of the domain map.
  454. */
  455. static void WriteDomainMap(DomainDir *data)
  456.   {
  457.   fprintf(fd, "%d %s %d\n", data->MapDir, data->Domain, data->HighFile);
  458.  
  459.   }
  460. /*
  461. * DomainFileAddResult()
  462. *
  463. * This function handles the result of a domain file addition.  This is paired
  464. * with DomainMailFileName() and should always be called after it has been
  465. * used to give out results.
  466. */
  467. void DomainFileAddResult(label DName, label system, label NodeId, char result)
  468.   {
  469.   DomainDir *data;
  470.   DOMAIN_FILE buf;
  471.   if ((data = GetDomain(DName, FALSE)) == NULL)
  472.     {
  473.     printf("URP IN DOMAINS!");
  474.     return;
  475.  
  476.     }
  477.   if (result == DOMAIN_SUCCESS)
  478.     {
  479.     if ((data->TargetSlot = SetUpCallOut(DName)) == ERROR)
  480.       {
  481.       #ifdef ROUTING_THIS_WAY
  482.       if (WeServe(DName) != NULL)
  483.         {
  484.         printf("Mail for unknown system %s.", system);
  485.         }
  486.       #else
  487.       if (WeServe(DName) != NULL)
  488.         {
  489.         /* since we're server, we don't search the secondary lists */
  490.         if (!FindTheNode(NodeId, system))
  491.           {
  492.           printf("Mail for unknown system %s.", system);
  493.  
  494.           }
  495.  
  496.         }
  497.       #endif
  498.       else if (data->HighFile == 1)
  499.         {
  500.         printf("Don't know how to reach domain %s.", DName);
  501.         }
  502.  
  503.       }
  504.     return;
  505.  
  506.     }
  507.   /* failure -- assume caller erases file */
  508.   if (--data->HighFile == 0)
  509.     {
  510.     /* this indicates empty dir */
  511.     MakeDomainFileName(buf, data->MapDir, "info");
  512.     unlink(buf);
  513.     KillDomainDirectory(data->MapDir);
  514.     KillData(&DomainMap, data->Domain); /* data's ptr is now invalid */
  515.  
  516.     }
  517.   UpdateMap();
  518.  
  519.   }
  520.  
  521. static SListBase KillDomains =
  522.   {
  523.   NULL, NULL, NULL, KillDomain, NULL
  524.  
  525.   };
  526. static char Change;
  527. /*
  528. * RationalizeDomains()
  529. *
  530. * This function goes through all the domains checking for empty domains and
  531. * domains with file lists with "holes" in them (caused by a node rejecting
  532. * some routemail -- won't happen in C-86 [probably], but there's nothing in
  533. * the rules to say it can't happen).  This should be called after each network
  534. * call involving domain-mail transmissions.
  535. */
  536. void RationalizeDomains()
  537.   {
  538.   Change = FALSE;
  539.   RunList(&DomainMap, DomainRationalize);
  540.   KillList(&KillDomains);       /* clears list and kills empty domains, too */
  541.   if (Change) UpdateMap();
  542.   DomainInit(FALSE);            /* reset temp call out flags */
  543.  
  544.   }
  545. /*
  546. * DomainRationalize()
  547. *
  548. * See RationalizeDomains for detail.
  549. *
  550. * NB: this could be made far more efficient someday by keeping track of the
  551. * last gap, or keeping a list of successfully sent mail files, or ... ?
  552. */
  553. static void DomainRationalize(DomainDir *data)
  554.   {
  555.   int rover, rover2, lasthit = -1;
  556.   char temp[10], temp2[10];
  557.   DOMAIN_FILE buffer, buffer2;
  558.   if (data->UsedFlag)
  559.     {
  560.     data->UsedFlag = FALSE;
  561.     Change = TRUE;
  562.     for (rover = 0; rover < data->HighFile; rover++)
  563.       {
  564.       sPrintf(temp, "%d", rover);
  565.       MakeDomainFileName(buffer, data->MapDir, temp);
  566.       if (access(buffer, 0) != 0)
  567.         {
  568.         /* hole found */
  569.         for (rover2 = rover + 1; rover2 < data->HighFile; rover2++)
  570.           {
  571.           sPrintf(temp2, "%d", rover2);
  572.           MakeDomainFileName(buffer2, data->MapDir, temp2);
  573.           if (access(buffer2, 0) == 0)
  574.             {
  575.             /* end of gap */
  576.             rename(buffer2, buffer);    /* rename file */
  577.             lasthit = rover;
  578.  
  579.             }
  580.  
  581.           }
  582.  
  583.         }
  584.       else lasthit = rover;
  585.  
  586.       }
  587.     if (lasthit == -1)
  588.       {
  589.       MakeDomainFileName(buffer, data->MapDir, "info");
  590.       unlink(buffer);
  591.       KillDomainDirectory(data->MapDir);
  592.       /* we can't kill the data right now 'cuz we're in list processing */
  593.       /* so we'll store it and kill it later */
  594.       AddData(&KillDomains, &data->MapDir, NULL, FALSE);
  595.  
  596.       }
  597.     else data->HighFile = lasthit + 1;
  598.  
  599.     }
  600.  
  601.   }
  602. /*
  603. * KillDomain()
  604. *
  605. * Kills a domain from DomainMap.
  606. */
  607. static void KillDomain(int *MapDir)
  608.   {
  609.   CheckNumber = TRUE;
  610.   KillData(&DomainMap, MapDir); /* KABOOM! */
  611.   CheckNumber = FALSE;
  612.  
  613.   }
  614. /*
  615. * FindDomainH()
  616. *
  617. * This finds the named domain handler on the domain handler list.
  618. */
  619. static void *FindDomainH(DomainHandler *data, char *name)
  620.   {
  621.   return (strCmpU(data->domain, name) == SAMESTRING) ? data : NULL;
  622.  
  623.   }
  624. /*
  625. * EatDomainH()
  626. *
  627. * This eats a line from ctdldmhd.sys.  Notice the support for the 'via'
  628. * capability.
  629. */
  630. static void *EatDomainH(char *line)
  631.   {
  632.   DomainHandler *data;
  633.   char *c;
  634.   label temp;
  635.   if ((c = strchr(line, '#')) != NULL) *c = 0;  /* kill '#' */
  636.   NormStr(line);
  637.   if (strlen(line) <= 3) return NULL;
  638.   /* if line is of format "domain via domain" then handle that */
  639.   /* characterized by lack of colon */
  640.   if ((c = strchr(line, ':')) == NULL)
  641.     {
  642.     return ViaHandle(line);
  643.  
  644.     }
  645.   if ((c = strchr(line, ' ')) == NULL)
  646.     {
  647.     printf("WARNING: badly formed ctdldmhd.sys (1:-%s-).\n", line);
  648.     return NULL;
  649.  
  650.     }
  651.   *c++ = 0;
  652.   if ((c = strchr(c, ':')) == NULL)
  653.     {
  654.     printf("WARNING: badly formed ctdldmhd.sys (2:-%s-).\n", c);
  655.     return NULL;
  656.  
  657.     }
  658.   c++;
  659.   data = GetDynamic(sizeof *data);
  660.   data->domain = strdup(line);
  661.   normId(c, temp);
  662.   data->nodeId = strdup(temp);
  663.   return data;
  664.  
  665.   }
  666. /*
  667. * ViaHandle()
  668. *
  669. * This handles a line of format "domain via domain" for EatDomainH().  It
  670. * always returns NULL since it's only setting up the link, not creating a
  671. * new record.
  672. */
  673. static void *ViaHandle(char *line)
  674.   {
  675.   /* we're guaranteed of no comments on line */
  676.   char *space, *via;
  677.   DomainHandler *target, *viadomain;
  678.   if ((space = strchr(line, ' ')) == NULL)
  679.     {
  680.     printf("WARNING: badly formed ctdldmhd.sys (3:-%s-).\n", line);
  681.     return NULL;
  682.  
  683.     }
  684.   *space++ = 0;
  685.   if (strncmp("via ", space, 4) != 0)
  686.     {
  687.     printf("WARNING: badly formed ctdldmhd.sys (4:-%s-).\n", space);
  688.     return NULL;
  689.  
  690.     }
  691.   /* no failure guaranteed ... */
  692.   via = strchr(space, ' ') + 1;
  693.   if ((target = SearchList(&DomainHandlers, line)) != NULL)
  694.     {
  695.     if ((viadomain = SearchList(&DomainHandlers, via)) != NULL)
  696.       {
  697.       target->via = viadomain;
  698.  
  699.       }
  700.  
  701.     }
  702.   /*
  703.   * since we aren't building a new record but instead are creating a link
  704.   * between two records, so we always return NULL.
  705.   */
  706.   return NULL;
  707.  
  708.   }
  709. /*
  710. * HandleExistingDomain()
  711. *
  712. * This is called to check to see if a domain with outstanding mail happens to
  713. * be served by this installation.  If so, then that mail must be processed:
  714. * mail for this system delivered to the designated users, mail for other
  715. * systems setup to be delivered to them if they can be identified.
  716. */
  717. void HandleExistingDomain(DomainDir *dir)
  718.   {
  719.   label  temp;
  720.   int            rover;
  721.   char   *domain;
  722.   DOMAIN_FILE  buffer;
  723.   SYS_FILE     newfn;
  724.   extern FILE  *netMisc;
  725.   label  nodeId, nodeName;
  726.   char   *c, oldNet;
  727.   extern char  RCount, SCount, *R_W_ANY;
  728.   extern char  inNet;
  729.   extern int   (*NetCharSource)(void);
  730.   extern int   RouteSlot;
  731.   /*
  732.   * we save the inNet state because this code may be called during a
  733.   * non-netting state (system init), while it works better when inNet
  734.   * is in a network state.  But this code is also called during network
  735.   * processing, too, so ... we have to push and pop the state or things
  736.   * may get screwy.  At least we lose the routing information (where a
  737.   * message came from) at the moment.
  738.   */
  739.   oldNet = inNet;
  740.   inNet = NORMAL_NET;           /* cheat */
  741.   if ((domain = SearchList(&Serves, dir->Domain)) != NULL)
  742.     {
  743.     for (rover = 0; rover < dir->HighFile; rover++)
  744.       {
  745.       sPrintf(temp, "%d", rover);
  746.       MakeDomainFileName(buffer, dir->MapDir, temp);
  747.       if ((netMisc = safeopen(buffer, READ_ANY)) != NULL)
  748.         {
  749.         getMsgStr(getNetChar, nodeId, NAMESIZE);
  750.         getMsgStr(getNetChar, nodeName, NAMESIZE);
  751.         NormStr(nodeId);
  752.         NormStr(nodeName);
  753.         for (c = nodeName; *c; c++)
  754.         if (*c == '_') *c = ' ';
  755.         /* first check against ourselves (!) */
  756.         if (RouteHere(nodeId, nodeName, domain))
  757.           {
  758.           getRoom(MAILROOM);
  759.           StartDecode(ReadRoutedDest);
  760.           RCount = SCount = 0;
  761.           NetCharSource = ReadRouted;
  762.           while (getMessage(ReadRouted, TRUE, TRUE, TRUE))
  763.             {
  764.             msgBuf.mbaddr[0] = 0;       /* just in case */
  765.             inRouteMail();
  766.  
  767.             }
  768.           NetCharSource = getNetChar;
  769.           fclose(netMisc);
  770.           unlink(buffer);
  771.           dir->UsedFlag = TRUE;
  772.           continue;
  773.  
  774.           }
  775.         fclose(netMisc);
  776.         if (AcceptRoute(nodeId, nodeName))
  777.           {
  778.           strCpy(nodeId, netTemp.netId);
  779.           FindRouteSlot();
  780.           /* kludge around a bug (does it exist anymore?) */
  781.           if (netTemp.nbHiRouteInd < 0) netTemp.nbHiRouteInd = 0;
  782.           sPrintf(temp, "R%d.%d", RouteSlot, netTemp.nbHiRouteInd++);
  783.           makeSysName(newfn, temp, &cfg.netArea);
  784.           MoveFile(buffer, newfn);      /* use system dep */
  785.           if ((netMisc = safeopen(newfn, R_W_ANY)) != NULL)
  786.             {
  787.             fprintf(netMisc, "%-20s", nodeId);
  788.             fclose(netMisc);
  789.  
  790.             }
  791.           netTemp.nbflags.HasRouted = TRUE;
  792.           putNet(RouteSlot, &netTemp);
  793.           dir->UsedFlag = TRUE;
  794.  
  795.           }
  796.  
  797.         }
  798.  
  799.       }
  800.  
  801.     }
  802.   inNet = oldNet ;
  803.  
  804.   }
  805. /*
  806. * SetCallout()
  807. *
  808. * For some domain with outstanding mail, find out what system we'll be
  809. * sending that mail to and setup that value.
  810. */
  811. void SetCallout(DomainDir *data)
  812.   {
  813.   data->TargetSlot = SetUpCallOut(data->Domain);
  814.  
  815.   }
  816. /*
  817. * SetUpCallOut()
  818. *
  819. * This is responsible for discovering what system should be called for
  820. * delivering mail to the designated domain.  It returns the index of the
  821. * node to receive the domain mail (whether final destination or simply a
  822. * transshipper).  If we serve the domain then return Error.
  823. *
  824. * NB: This is an interface function to the routines which actually handle
  825. * setting the proper call out flags and returning the proper index.  The
  826. * function itself initializes the data structure for the search (by
  827. * resetting flags in the DomainHandlers list) and then calling something
  828. * else to conduct the search.  This structure is called for due to the
  829. * recursive nature of the search.
  830. */
  831. void ClearSearched(DomainHandler *Domain);
  832. static int SetUpCallOut(char *DName)
  833.   {
  834.   RunList(&DomainHandlers, ClearSearched);      /* clears searched flag */
  835.   return CallOutWork(DName);            /* do possibly recursive work   */
  836.  
  837.   }
  838. /*
  839. * CallOutWork()
  840. *
  841. * This does actual work of SetUpCallOut().  This is separated from the
  842. * calling function because we want to modularize the actual search ability
  843. * from the callers, but we must clear the search flags of the list once.
  844. * Since recursion is employed to follow the links of the 'via' pointers,
  845. * we are forced to this measure.
  846. *
  847. * Oh, yes.  This function searches for the domain handler responsible for
  848. * the given domain.  If not found, we use the MailHub designee as the
  849. * recipient of this domain mail.  If found, it then tries to find if we
  850. * connect directly with that server.  If so, we set the domain mail call
  851. * out flag for that node and return the node's index to the caller.  If we
  852. * don't connect directly (i.e., node is not in the list), then we try to
  853. * follow the 'via' pointers until a connection is found.  If never found,
  854. * then use the MailHub, otherwise use and return the found node.
  855. *
  856. * If we serve the domain, return ERROR.
  857. *
  858. * We use the search flag in each element of the DomainHandler's list to
  859. * prevent "infinite recursion" syndrome.
  860. */
  861. static int CallOutWork(char *DName)
  862.   {
  863.   DomainHandler *Domain;
  864.   UNS_16 Slot;
  865.   extern int RouteSlot;
  866.   if (WeServe(DName) != NULL) return ERROR;
  867.   if ((Domain = SearchList(&DomainHandlers, DName)) != NULL)
  868.     {
  869.     if ((RouteSlot = searchNet(Domain->nodeId, &netTemp)) != ERROR)
  870.       {
  871.       DomainFlags[Slot = FindRouteSlot()] = TRUE;
  872.       return (int) Slot;        /* and get out now */
  873.  
  874.       }
  875.  
  876.     }
  877.   /*
  878.   * if we've searched this node before then we're in a loop and had
  879.   * better stop.  We handle the if clause this way as most efficient.
  880.   */
  881.   if (Domain != NULL && !Domain->searched)
  882.     {
  883.     /* if a "via" is known, trace it */
  884.     Domain->searched = TRUE;    /* infinite loop vaccine */
  885.     if (Domain != NULL && Domain->via != NULL)
  886.       {
  887.       return (int)CallOutWork(Domain->via->domain);     /* recurse it */
  888.  
  889.       }
  890.  
  891.     }
  892.   else if (Domain != NULL && Domain->searched)
  893.     {
  894.     printf("Loop detected\n");
  895.  
  896.     }
  897.   /* unknown hub or domain -> mailhub */
  898.   if (cfg.MailHub != 0)
  899.     {
  900.     if ((RouteSlot = searchNameNet(cfg.codeBuf + cfg.MailHub, &netTemp))
  901.     != ERROR)
  902.       {
  903.       DomainFlags[Slot = FindRouteSlot()] = TRUE;
  904.       return (int) Slot;        /* and get out now */
  905.  
  906.       }
  907.  
  908.     }
  909.   return (int)ERROR;
  910.  
  911.   }
  912. /*
  913. * ClearSearched()
  914. *
  915. * Work function to clear the search field of a domain handler.
  916. */
  917. void ClearSearched(DomainHandler *Domain)
  918.   {
  919.   Domain->searched = FALSE;
  920.  
  921.   }
  922. static int DMCount;
  923. static char LCheck;     /* groan -- inadequacy of list processing */
  924. /*
  925. * DomainOut()
  926. *
  927. * This function is tasked with sending all domain mail to the current system
  928. * we're connected to that is destined for that system.  Actually, it just
  929. * cycles through all current domains and lets another function handle the
  930. * real work.
  931. */
  932. int DomainOut(char LocalCheck)
  933.   {
  934.   void SendDomainMail();
  935.   LCheck = LocalCheck;
  936.   DMCount = 0;
  937.   RunList(&DomainMap, SendDomainMail);
  938.   return DMCount;
  939.  
  940.   }
  941. /*
  942. * SendDomainMail()
  943. *
  944. * This does the actual work of sending the mail.  It cycles through all the
  945. * mail in the given domain's directory, sending each piece in sequence.
  946. * Rejected mail, if any, is kept for another try later, and later processing
  947. * will close up any "holes" left in the numerical naming sequence for mail
  948. * files.  That same later processing will also kill empty directories.
  949. */
  950. void SendDomainMail(DomainDir *data)
  951.   {
  952.   DOMAIN_FILE buffer;
  953.   int rover, result;
  954.   label temp, Id, Name;
  955.   if (!gotCarrier()) return;
  956.   /* This domain is to be delivered to current system? */
  957.   if (data->TargetSlot == thisNet)
  958.     {
  959.     data->UsedFlag = TRUE;
  960.     for (rover = 0; rover < data->HighFile; rover++)
  961.       {
  962.       sPrintf(temp, "%d", rover);
  963.       MakeDomainFileName(buffer, data->MapDir, temp);
  964.       if ((result = SendRouteMail(buffer, data->Domain, Id, Name, LCheck))
  965.       == GOOD_SEND)
  966.         {
  967.         unlink(buffer);
  968.         DMCount++;
  969.  
  970.         }
  971.       else if (result == REFUSED_ROUTE)
  972.         {
  973.         printf("Domain mail for %s _ %s not accepted by %s.",
  974.         Name, data->Domain, netBuf.netName);
  975.  
  976.         }
  977.  
  978.       }
  979.  
  980.     }
  981.  
  982.   }
  983. /*
  984. * SystemInSecondary()
  985. *
  986. * This looks for the given system in the secondary lists.  If a domain
  987. * was specified then just return almost immediately.
  988. */
  989. char SystemInSecondary(char *Name, char *Domain, char *dup)
  990.   {
  991.   int rover;
  992.   char *sep;
  993.   SYS_FILE secondary;
  994.   label name;
  995.   char WorkName[(NAMESIZE * 2) + 1];
  996.   char SearchSecondary(char *secondary,char *Name,char *Domain,char *isdup);
  997.   strCpy(WorkName, Name);
  998.   /* is the domain specified already?  if so, parse it */
  999.   if ((sep = strchr(WorkName, '_')) != NULL ||
  1000.   (sep = strchr(WorkName, '.')) != NULL)
  1001.     {
  1002.     *sep++ = 0;
  1003.     NormStr(WorkName);
  1004.     NormStr(sep);
  1005.     if (strchr(sep, '_') != NULL ||
  1006.     strchr(sep, '.') != NULL)
  1007.     return FALSE;       /* no subdomains */
  1008.     if (strLen(sep) < 1) return FALSE;
  1009.     strCpy(Name, WorkName);
  1010.     strCpy(Domain, sep);
  1011.     *dup = FALSE;               /* by definition */
  1012.     return TRUE;
  1013.  
  1014.     }
  1015.   Domain[0] = 0;
  1016.   for (rover = 0; rover < 100; rover++)
  1017.     {
  1018.     sPrintf(name, "nodes%d.fst", rover);
  1019.     makeSysName(secondary, name, &cfg.netArea);
  1020.     if (access(secondary, 0) != 0) break;
  1021.     if (SearchSecondary(secondary, Name, Domain, dup)) break;
  1022.  
  1023.     }
  1024.   strCpy(WorkName, Name);
  1025.   /* make sure we found something and it's not us */
  1026.   return (char)( (Domain[0] != 0 &&
  1027.   strCmpU(Name, cfg.codeBuf + cfg.nodeName) != SAMESTRING &&
  1028.   strCmpU(UseNetAlias(WorkName,FALSE), cfg.codeBuf + cfg.nodeName)
  1029.   != SAMESTRING));
  1030.  
  1031.   }
  1032. /*
  1033. * SearchSecondary()
  1034. *
  1035. * This does the actual work of searching a secondary list for a system.
  1036. */
  1037. char SearchSecondary(char *secondary,char *Name,char *Domain,char *isdup)
  1038.   {
  1039.   FILE *fd;
  1040.   int  bucket;
  1041.   char found, *tab = NULL, *c, *tab2 = NULL;
  1042.   char line[90];
  1043.   JumpInfo JumpTable[BUCKETCOUNT];
  1044.   if ((fd = fopen(secondary, READ_ANY)) == NULL)    return FALSE;
  1045.   fread(line, VERS_SIZE + 1, 1, fd);
  1046.   fread(JumpTable, sizeof JumpTable, 1, fd);
  1047.   #ifdef IS_MOTOROLA
  1048.   for (bucket = 0; bucket < BUCKETCOUNT; bucket++)  Intel32ToMotorola(&JumpTable[bucket].offset);
  1049.   #endif
  1050.   bucket = (isdigit(Name[0])) ? Name[0] - '0' :
  1051.   toUpper(Name[0]) - 'A' + 10;
  1052.   fseek(fd, JumpTable[bucket].offset, 0);
  1053.   found = FALSE;
  1054.   do
  1055.     {
  1056.     *isdup = FALSE;
  1057.     if (fgets(line, sizeof line, fd) == NULL) break;
  1058.     if ((tab2 = strchr(line, '\n')) != NULL)
  1059.     if( tab2 )*tab2 = 0;
  1060.     if (strlen(line) == 0)
  1061.       {
  1062.       break;
  1063.  
  1064.       }
  1065.     if (line[0] <= ' ')
  1066.       {
  1067.       switch (line[0])
  1068.         {
  1069.         case DUP:
  1070.         *isdup = TRUE;
  1071.         break;
  1072.         default: printf("Ooop!");
  1073.         break;
  1074.  
  1075.         }
  1076.       c = line + 1;
  1077.  
  1078.       }
  1079.     else c = line;
  1080.     tab = strchr(c, '\t');
  1081.     if( tab )*tab++ = 0;
  1082.     if (strCmpU(c, Name) == 0)
  1083.     found = TRUE;
  1084.     if (strCmpU(c, Name) > 0) break;
  1085.  
  1086.     }
  1087.   while (!found);
  1088.   if (found)
  1089.     {
  1090.     if ((tab2 = strchr(tab, '\t')) != NULL)
  1091.     if( tab2 )*tab2++ = 0;
  1092.     strCpy(Domain, tab);
  1093.     if (tab2 != NULL)    /* alias?  Copy it into search string */
  1094.     strCpy(Name, tab2);
  1095.  
  1096.     }
  1097.   fclose(fd);
  1098.   return found;
  1099.  
  1100.   }
  1101. /*
  1102. * EatCosts()
  1103. *
  1104. * This function eats a line from ctdlcost.sys and returns a structure
  1105. * containing the cost suitable for inclusion into a list.
  1106. */
  1107. static void *EatCosts(char *line)
  1108.   {
  1109.   NumToString *data;
  1110.   char *c;
  1111.   if ((c = strchr(line, ' ')) == NULL) return NULL;
  1112.   *c++ = 0;
  1113.   if (strCmpU(line, "Unknown") == SAMESTRING)
  1114.     {
  1115.     UnknownCost = atoi(c);
  1116.     return NULL;
  1117.  
  1118.     }
  1119.   data = GetDynamic(sizeof *data);
  1120.   data->string = strdup(line);
  1121.   data->num = atoi(c);
  1122.   return data;
  1123.  
  1124.   }
  1125. /*
  1126. * FindCost()
  1127. *
  1128. * This finds the cost of sending mail to a given domain.
  1129. */
  1130. UNS_16 FindCost(char *domain)
  1131.   {
  1132.   UNS_16 *data;
  1133.   if ((data = SearchList(&Costs, domain)) == NULL)
  1134.   return UnknownCost;
  1135.   return *data;
  1136.  
  1137.   }
  1138. /*
  1139. * WriteDomainContents()
  1140. *
  1141. * This writes the secondary list in readable format by reading NODES*.RAW.
  1142. */
  1143. void WriteDomainContents()
  1144.   {
  1145.   int rover;
  1146.   SYS_FILE secondary;
  1147.   label name;
  1148.   int   count = 0;
  1149.   char  line[80], vers[VERS_SIZE + 1];
  1150.   char  work[80], *c;
  1151.   FILE *fd, *fd2;
  1152.   extern char *READ_TEXT;
  1153.   for (rover = 0; rover < 100; rover++)
  1154.     {
  1155.     sPrintf(name, "nodes%d.raw", rover);
  1156.     makeSysName(secondary, name, &cfg.netArea);
  1157.     if ((fd = fopen(secondary, READ_TEXT)) == NULL)
  1158.       {
  1159.       break;
  1160.  
  1161.       }
  1162.     /* make sure the raw version genned the fst version */
  1163.     sPrintf(name, "nodes%d.fst", rover);
  1164.     makeSysName(secondary, name, &cfg.netArea);
  1165.     if ((fd2 = fopen(secondary, READ_ANY)) == NULL)
  1166.       {
  1167.       fclose(fd);
  1168.       continue;
  1169.  
  1170.       }
  1171.     GetAString(line, 80, fd);   /* gets version line */
  1172.     fread(vers, 1, VERS_SIZE, fd2);
  1173.     vers[VERS_SIZE] = 0;
  1174.     fclose(fd2);
  1175.     if (strCmpU(line, vers) != SAMESTRING)
  1176.       {
  1177.       fclose(fd);
  1178.       continue;
  1179.  
  1180.       }
  1181.     while (GetAString(line, 80, fd) != NULL)
  1182.       {
  1183.       if (strncmp(line, ".domain", 7) == SAMESTRING)
  1184.         {
  1185.         if (count != 0) doCR();
  1186.         strCpy(work, strchr(line, ' ') + 1);
  1187.         if ((c = strchr(work, ' ')) != NULL) *c = 0;
  1188.         doCR();
  1189.         count = 0;
  1190.         mPrintf("In domain %s", strchr(line, ' ') + 1);
  1191.         if (FindCost(work) != 0)
  1192.         mPrintf(" (%d LD credits):", FindCost(work));
  1193.         doCR();
  1194.  
  1195.         }
  1196.       else
  1197.         {
  1198.         if (strchr(line, ':') != NULL) continue;
  1199.         NormStr(line);
  1200.         if (strLen(line) == 0) continue;
  1201.         mPrintf("%s", line);
  1202.         if (++count % 3 == 0)
  1203.           {
  1204.           count = 0;
  1205.           doCR();
  1206.  
  1207.           }
  1208.         else
  1209.           {
  1210.           #ifdef TURBO_C_VSPRINTF_BUG
  1211.           SpaceBug(28 - strLen(line));   /* EEEESH */
  1212.           #else
  1213.           mPrintf("%*c", 28 - strLen(line), ' ');
  1214.           #endif
  1215.  
  1216.           }
  1217.  
  1218.         }
  1219.  
  1220.       }
  1221.     if (count != 0) doCR();
  1222.     doCR();
  1223.     fclose(fd);
  1224.  
  1225.     }
  1226.  
  1227.   }
  1228. /*
  1229. * DomainLog()
  1230. *
  1231. * This writes out a message to the domain log.
  1232. */
  1233. static void DomainLog(char *str)
  1234.   {
  1235.   extern SListBase Errors;
  1236.   printf("(%s %s) %s", formDate(), Current_Time(), str);
  1237.   }
  1238. /*
  1239. * RouteHere()
  1240. *
  1241. * Returns true if the mail is meant for this installation.
  1242. */
  1243. char RouteHere(char *Id, char *Name, char *Domain)
  1244.   {
  1245.   label temp1;
  1246.   char *c;
  1247.   extern label HomeId;
  1248.   normId(Id, temp1);
  1249.   if (strCmpU(temp1, HomeId) == SAMESTRING) return TRUE;
  1250.   if (!normId(Id, temp1))
  1251.     {
  1252.     strCpy(temp1, Name);
  1253.     for (c = temp1; *c; c++)
  1254.     if (*c == '_') *c = ' ';
  1255.     if ((Domain == NULL ||
  1256.     strCmpU(Domain, cfg.codeBuf + cfg.nodeDomain) == SAMESTRING ||
  1257.     WeServe(Domain) != NULL) &&
  1258.     (strCmpU(cfg.codeBuf + cfg.nodeName, Name) == SAMESTRING ||
  1259.     strCmpU(cfg.codeBuf + cfg.nodeName,
  1260.     UseNetAlias(Name, FALSE)) == SAMESTRING))
  1261.     return TRUE;
  1262.  
  1263.     }
  1264.   return FALSE;
  1265.  
  1266.   }
  1267. /*
  1268. * LocalName()
  1269. *
  1270. * This function takes a string of form <system> _ <domain> and attempts to
  1271. * discover if this domain mapped system is actually a local.  This is used
  1272. * when we're sending mail and are trying to find out if a Who Else override
  1273. * needs to be generated.  Ugly  kludge, but, hey, that's what programming's
  1274. * all about, eh?
  1275. */
  1276. char *LocalName(char *system)
  1277.   {
  1278.   char *domain, *System;
  1279.   if ((domain = strchr(system, '_')) == NULL) return system;
  1280.   domain += 2;  /* always preceded by a space -- or so we assume */
  1281.   if (strCmpU(domain, cfg.codeBuf + cfg.nodeDomain) == SAMESTRING ||
  1282.   WeServe(domain) != NULL)
  1283.     {
  1284.     System = strdup(system);
  1285.     if ((domain = strchr(System, ' ')) == NULL)
  1286.     return system;      /* should never happen, though */
  1287.     *domain = NULL;
  1288.     if (searchNameNet(System, &netTemp) != ERROR)
  1289.       {
  1290.       free(System);
  1291.       return netTemp.netName;
  1292.  
  1293.       }
  1294.     free(System);
  1295.  
  1296.     }
  1297.   return system;
  1298.  
  1299.   }
  1300. /*
  1301. * lifo()
  1302. *
  1303. * Universal function for causing Last In First Out lists to occur in the
  1304. * SList stuff.
  1305. */
  1306. static int lifo()
  1307.   {
  1308.   return 1;
  1309.  
  1310.   }
  1311.  
  1312.  
  1313. /*
  1314. * SendRouteMail()
  1315. *
  1316. * This function will send route mail.
  1317. */
  1318. char SendRouteMail(char *filename, char *domainname, char *Tid, char *Tname,
  1319. char LocalCheck)
  1320.   {
  1321.   struct cmd_data cmds;
  1322.   char work[(2 * NAMESIZE) + 10];
  1323.   label ThisId;
  1324.   normId(netBuf.netId, ThisId);
  1325.   if ((netMisc = safeopen(filename, READ_ANY)) != NULL)
  1326.     {
  1327.     getMsgStr(getNetChar, cmds.fields[0], NAMESIZE);
  1328.     getMsgStr(getNetChar, cmds.fields[1], NAMESIZE);
  1329.     if (!normId(cmds.fields[0], Tid) || strLen(Tid) == 0)
  1330.     strCpy(cmds.fields[0], "  ");
  1331.     else
  1332.     strCpy(cmds.fields[0], Tid);
  1333.     if (LocalCheck)
  1334.     if (!netBuf.nbflags.Stadel && strCmpU(Tid,ThisId) != SAMESTRING)
  1335.       {
  1336.       fclose(netMisc);
  1337.       return REFUSED_ROUTE; /* actually, just a lie */
  1338.  
  1339.       }
  1340.     NormStr(cmds.fields[1]);
  1341.     strCpy(Tname, cmds.fields[1]);    /* reporting purposes */
  1342.     strCpy(cmds.fields[2], domainname); /* just for luck        */
  1343.     cmds.command = ROUTE_MAIL;
  1344.     if (LocalCheck || sendNetCommand(&cmds, "Route Mail"))
  1345.       {
  1346.       if (LocalCheck || ITL_SendMessages())
  1347.         {
  1348.         StartDecode(ReadRoutedDest);
  1349.         RCount = SCount = 0;
  1350.         NetCharSource = ReadRouted;
  1351.         sPrintf(work,
  1352.         (strLen(domainname) != 0) ? "%s _ %s" : "%s%s",
  1353.         cmds.fields[1], domainname);
  1354.         while (getMessage(ReadRouted, TRUE, FALSE, TRUE))
  1355.         if (netBuf.nbflags.Stadel)
  1356.         prStStyle(1, Tname, sendITLchar, domainname);
  1357.         else
  1358.         prNetStyle(1, sendITLchar, TRUE, work);
  1359.         NetCharSource = getNetChar;
  1360.         fclose(netMisc);
  1361.         if (!LocalCheck) ITL_StopSendMessages();
  1362.         if (TrError == TRAN_SUCCESS)
  1363.         return GOOD_SEND;
  1364.         else
  1365.         return UNKNOWN_ERROR;
  1366.  
  1367.         }
  1368.  
  1369.       }
  1370.     else
  1371.       {
  1372.       fclose(netMisc);
  1373.       return REFUSED_ROUTE;
  1374.  
  1375.       }
  1376.  
  1377.     }
  1378.   return NO_SUCH_FILE;
  1379.  
  1380.   }
  1381.